//
//  HLWebViewController.m
//  YueYue
//
//  Created by hulu001 on 2023/5/17.
//

#import "HLWebViewController.h"
#import <WebKit/WebKit.h>
#import "HLShareContainerView.h"
// WKWebView 内存不释放的问题解决
@interface HLWeakWebViewScriptMessageDelegate : NSObject<WKScriptMessageHandler>

//WKScriptMessageHandler 这个协议类专门用来处理JavaScript调用原生OC的方法
@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;

@end


@interface HLWebViewController ()<WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate, UINavigationControllerBackButtonHandlerProtocol>

@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) UIProgressView * progressView;
@property (nonatomic, strong) NSString *urlStr;
@property (nonatomic, assign) BOOL hiddenNavBar;

@end

@implementation HLWebViewController

- (instancetype)initWithUrl:(NSString *)url hiddenNav:(BOOL)isHiddenNav
{
    self = [super init];
    
    if (self) {
        self.urlStr = url;
        self.hiddenNavBar = isHiddenNav;
    }
    return self;
}

#pragma mark - Override
- (void)viewDidLoad {
    [super viewDidLoad];
    NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
    NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
    }];
    
    MJWeakSelf
    // 添加监测网页加载进度的观察者
    [self.KVOController observe:self.webView keyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        CGFloat progress = [change[NSKeyValueChangeNewKey] floatValue];
        if (progress > 0){
            weakSelf.progressView.hidden = NO;
            weakSelf.progressView.progress = progress;
            if (progress > 0.99){
                weakSelf.progressView.hidden = YES;
            }
        }
    }];
    
    [self.KVOController observe:self.webView keyPath:@"title" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        weakSelf.title = change[NSKeyValueChangeNewKey];
    }];
}

- (void)initSubviews
{
    [super initSubviews];
    
    [self.view addSubview:self.webView];
    [self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
    [self.view addSubview:self.progressView];
    [self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.mas_equalTo(0);
        make.top.mas_equalTo(NavigationContentTopConstant);
    }];
}


- (UIImage *)qmui_navigationBarBackgroundImage
{
    if (self.hiddenNavBar){
        return [UIImage qmui_imageWithColor:UIColorClear];
    }
    return [UIImage qmui_imageWithColor:UIColorWhite];
}

- (UIStatusBarStyle)preferredStatusBarStyle
{
    if (self.hiddenNavBar){
        return UIStatusBarStyleLightContent;
    }
    return UIStatusBarStyleDarkContent;
}

- (nullable UIColor *)qmui_navigationBarTintColor
{
    if (self.hiddenNavBar){
        return UIColorWhite;
    }
    return UIColorBlack;
}

- (nullable UIColor *)qmui_titleViewTintColor
{
    if (self.hiddenNavBar){
        return UIColorWhite;
    }
    return UIColorBlack;
}



- (BOOL)shouldPopViewControllerByBackButtonOrPopGesture:(BOOL)byPopGesture
{
    if (self.webView.canGoBack) {
        [self.webView goBack];
        return NO;
    }
    return YES;
}
- (BOOL)forceEnableInteractivePopGestureRecognizer
{
    return NO;
}

//- (void)navigationController:(nonnull QMUINavigationController *)navigationController
//poppingByInteractiveGestureRecognizer:(nullable UIScreenEdgePanGestureRecognizer *)gestureRecognizer
//                 isCancelled:(BOOL)isCancelled
// viewControllerWillDisappear:(nullable UIViewController *)viewControllerWillDisappear
//    viewControllerWillAppear:(nullable UIViewController *)viewControllerWillAppear
//{
//    if (self.webView.canGoBack) {
//        [self.webView goBack];
//    }
//}

- (void)backViewController
{
    // 返回完H5内页后再返回原生页面
    if (self.webView.canGoBack) {
        [self.webView goBack];
    } else {
        //TODO: 导航返回前一页
        [self.navigationController popViewControllerAnimated:YES];
    }
}


- (void)refreshAction:(id)sender{
    [_webView reload];
}

#pragma mark - Getter
- (UIProgressView *)progressView {
    if (!_progressView){
        _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, NavigationBarHeight + 2, self.view.frame.size.width, 2)];
        _progressView.tintColor = [UIColor blueColor];
        _progressView.trackTintColor = [UIColor clearColor];
    }
    return _progressView;
}
- (WKWebView *)webView{
    if(_webView == nil){
        
        //创建网页配置对象
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        
        // 创建设置对象
        WKPreferences *preference = [[WKPreferences alloc]init];
        //最小字体大小 当将javaScriptEnabled属性设置为NO时，可以看到明显的效果
        preference.minimumFontSize = 0;
        //设置是否支持javaScript 默认是支持的
        preference.javaScriptEnabled = YES;
        // 在iOS上默认为NO，表示是否允许不经过用户交互由javaScript自动打开窗口
        preference.javaScriptCanOpenWindowsAutomatically = YES;
        config.preferences = preference;
        
        // 是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
        config.allowsInlineMediaPlayback = YES;
        //设置视频是否需要用户手动播放  设置为NO则会允许自动播放
        config.mediaTypesRequiringUserActionForPlayback = YES;
        //设置是否允许画中画技术 在特定设备上有效
        config.allowsPictureInPictureMediaPlayback = YES;
        //设置请求的User-Agent信息中应用程序名称 iOS9后可用
        config.applicationNameForUserAgent = @"YueLian";
        
        //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
        HLWeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[HLWeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
        
        //这个类主要用来做native与JavaScript的交互管理
        WKUserContentController * wkUController = [[WKUserContentController alloc] init];
        config.userContentController = wkUController;
        
        //以下代码适配文本大小
        NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
        //用于进行JavaScript注入
        WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
        [config.userContentController addUserScript:wkUScript];
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlgoTosharePop"];
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlAppJumpAction"];
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlAppJumpAction2"];
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlgoToWithdraw"];
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlgoToUserHomeVC"];
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlgoToSessionVC"];
        
        // 去客服中心
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlgoCustomerAction"];
        // 返回
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlgoBackAction"];
        // 打开相册，上传头像
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlSelectAlbumPictureAndUploadJumpAction"];
        // 金币充值半弹窗关闭方法
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmlCloseModalJumpAction"];
        // 跳转到视频匹配页面
        [config.userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"htmljumpVideoMatchingJumpAction"];
        
        _webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
        if (self.hiddenNavBar){
            [_webView.scrollView  setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
            _webView.scrollView.showsVerticalScrollIndicator = NO;
        }
        // UI代理
        _webView.UIDelegate = self;
        // 导航代理
        _webView.navigationDelegate = self;
        // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回
        _webView.allowsBackForwardNavigationGestures = YES;
        //可返回的页面列表, 存储已打开过的网页
//        WKBackForwardList * backForwardList = [_webView backForwardList];
        
        if (self.urlStr.length > 4) {
            BOOL result = [[self.urlStr substringToIndex:4] compare:@"http" options:NSCaseInsensitiveSearch];
            if (result) {
                self.urlStr = [NSString stringWithFormat:@"https://%@",self.urlStr];
            }
        }
//        [self getCookie];
        NSString *url = self.urlStr?:@"";
//        NSDictionary *dict = YYHTTPManager.sharedInstance.requestSerializer.HTTPRequestHeaders;
//        NSString *params = [@"?params=" stringByAppendingString:dict.mj_JSONString];
//        url = [url stringByAppendingString:params];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
        [_webView loadRequest:request];
        
    }
    return _webView;
}

//解决第一次进入的cookie丢失问题
- (NSString *)readCurrentCookieWithDomain:(NSString *)domainStr{
    NSHTTPCookieStorage*cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSMutableString * cookieString = [[NSMutableString alloc]init];
    for (NSHTTPCookie*cookie in [cookieJar cookies]) {
        if (@available(iOS 11.0, *)) {
            [[WKWebsiteDataStore defaultDataStore].httpCookieStore setCookie:cookie completionHandler:^{
            }];
        } else {
            // Fallback on earlier versions
        }
        [cookieString appendFormat:@"%@=%@;",cookie.name,cookie.value];
    }
    
    //删除最后一个“;”
    if ([cookieString hasSuffix:@";"]) {
        [cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
    }
    return cookieString;
}

//解决 页面内跳转（a标签等）还是取不到cookie的问题
- (void)getCookie{
    
    //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    //js函数
    NSString *JSFuncString =
    @"function setCookie(name,value,expires)\
    {\
    var oDate=new Date();\
    oDate.setDate(oDate.getDate()+expires);\
    document.cookie=name+'='+value+';expires='+oDate+';path=/'\
    }\
    function getCookie(name)\
    {\
    var arr = document.cookie.match(new RegExp('(^| )'+name+'=([^;]*)(;|$)'));\
    alert(getCookie('PHPSESSID'));\
    if(arr != null) return unescape(arr[2]); return null;\
    }\
    function delCookie(name)\
    {\
    var exp = new Date();\
    exp.setTime(exp.getTime() - 1);\
    var cval=getCookie(name);\
    if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\
    }";
    
    //拼凑js字符串
    NSMutableString *JSCookieString = JSFuncString.mutableCopy;
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];
        [JSCookieString appendString:excuteJSString];
    }
    //执行js
    [_webView evaluateJavaScript:JSCookieString completionHandler:nil];
    
}

//被自定义的WKScriptMessageHandler在回调方法里通过代理回调回来，绕了一圈就是为了解决内存不释放的问题
//通过接收JS传出消息的name进行捕捉的回调方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    //用message.body获得JS传出的参数体
    if ([message.name isEqualToString:@"htmlgoTosharePop"]) {
        [HLShareContainerView showWithCallback:^(NSString *item) {
            
        }];
    } else if ([message.name isEqualToString:@"htmlgoToWithdraw"]) {
        // 提现
    } else if ([message.name isEqualToString:@"htmlAppJumpAction"]) {
        [HLUIHelper jumpWithKey:message.body param:@""];
    } else if ([message.name isEqualToString:@"htmlgoToUserHomeVC"]) { // 去首页
        NSString *num = message.body;
        if (num.length) {
        }
    } else if ([message.name isEqualToString:@"htmlgoToSessionVC"]) { // 去会话
        NSString *num = message.body;
        if (num.length) {
            
        }
    } else if ([message.name isEqualToString:@"htmlgoCustomerAction"]) {
        // // 去客服中心
    } else if ([message.name isEqualToString:@"htmlgoBackAction"]) {
        if (self.navigationController) {
            [self.navigationController popViewControllerAnimated:YES];
        } else {
            [self dismissViewControllerAnimated:YES completion:^{
            }];
        }
    } else if ([message.name isEqualToString:@"htmlSelectAlbumPictureAndUploadJumpAction"]) {
        // 打开相册，上传头像
    } else if ([message.name isEqualToString:@"htmlCloseModalJumpAction"]) {
        // 金币充值半弹窗关闭方法
    } else if ([message.name isEqualToString:@"htmljumpVideoMatchingJumpAction"]) {
        // 跳转到视频匹配页面
    } else if ([message.name isEqualToString:@"htmlAppJumpAction2"]) {
        NSDictionary *dict = message.body;
        NSString *inturncode = dict[@"code"];
        NSString *url = dict[@"url"];
        
        if (url.length && inturncode.length) {
            [HLUIHelper jumpWithKey:inturncode param:url];
        }
    }
}

#pragma mark - WKNavigationDelegate
/*
 WKNavigationDelegate主要处理一些跳转、加载处理操作，WKUIDelegate主要处理JS脚本，确认框，警告框等
 */

// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    if ([webView canGoBack]) {
        //self.navigationItem.leftBarButtonItems = @[self.backItem, self.closeItem];
    } else {
        //self.navigationItem.leftBarButtonItems = @[self.backItem];
    }
}

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    [self.progressView setProgress:0.0f animated:NO];
}

// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}

// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
//    [self getCookie];
}

//提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    [self.progressView setProgress:0.0f animated:NO];
}

// 接收到服务器跳转请求即服务重定向时之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}

// 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSString * urlStr = navigationAction.request.URL.absoluteString;
    NSLog(@"发送跳转请求：%@",urlStr);
    NSArray *array = @[@"w",@"e",@"i",@"x",@"i",@"n",@":",@"/",@"/"];
    NSString *result = [array componentsJoinedByString:@""];
    if ([urlStr hasPrefix:result]) {
        if (@available(iOS 10.0, *)) {
            [[UIApplication sharedApplication] openURL:navigationAction.request.URL options:@{} completionHandler:^(BOOL success) {
                
            }];
        } else {
            // Fallback on earlier versions
            [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
        }
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    else
    {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

// 根据客户端受到的服务器响应头以及response相关信息来决定是否可以跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSString * urlStr = navigationResponse.response.URL.absoluteString;
    NSLog(@"[debug] decidePolicyForNavigationAction\n%@", navigationResponse.description);
    NSLog(@"[debug] decidePolicyForNavigationAction\n%@", urlStr);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
}

//需要响应身份验证时调用 同样在block中需要传入用户身份凭证
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
    
    //用户身份信息
    NSURLCredential * newCred = [[NSURLCredential alloc] initWithUser:@"user123" password:@"123" persistence:NSURLCredentialPersistenceNone];
    //为 challenge 的发送方提供 credential
    [challenge.sender useCredential:newCred forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
    
}

//进程被终止时调用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
    
}

#pragma mark - WKUIDelegate

/**
 *  web界面中有弹出警告框时调用
 *
 *  @param webView           实现该代理的webview
 *  @param message           警告框中的内容
 *  @param completionHandler 警告框消失调用
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的弹出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}
// 确认框
//JavaScript调用confirm方法后回调的方法 confirm是js中的确定框，需要在block中把用户选择的情况传递进去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}

#pragma mark - 需要同步返回值给JS的 原生方法
// 输入框
//JavaScript调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    if ([prompt isEqualToString:@"iosIsLogin"]) {
        // ios是否登陆
    } else if ([prompt isEqualToString:@"isShare"]) {
        // 是否允许显示网页分享按钮
    } else if ([prompt isEqualToString:@"isAppleReview"]) {
        // 是否正在审核状态
    } else if ([prompt isEqualToString:@"getUserInfo"]) {
        // 向H5传递用户数据
    }
}
// 页面是弹出窗口 _blank 处理
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    if (!navigationAction.targetFrame.isMainFrame) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - <调用JS>


- (NSString *)noWhiteSpaceString : (NSString *)str {
    NSString *newString = str;
  //去除掉首尾的空白字符和换行字符
    newString = [newString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    newString = [newString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    newString = [newString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];  //去除掉首尾的空白字符和换行字符使用
    newString = [newString stringByReplacingOccurrencesOfString:@" " withString:@""];
//    可以去掉空格，注意此时生成的strUrl是autorelease属性的，所以不必对strUrl进行release操作！
    return newString;
}

#pragma mark - setter

#pragma mark - YDPPickerViewDelegate
- (void)updateData {
    [self.webView reload];
}
/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end



@implementation HLWeakWebViewScriptMessageDelegate

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
    self = [super init];
    if (self) {
        _scriptDelegate = scriptDelegate;
    }
    return self;
}

#pragma mark - WKScriptMessageHandler
//遵循WKScriptMessageHandler协议，必须实现如下方法，然后把方法向外传递
//通过接收JS传出消息的name进行捕捉的回调方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    
    if ([self.scriptDelegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
        [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
    }
}

@end
